package tech.summerly.weather.db

import android.arch.persistence.room.Room
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import kotlinx.coroutines.experimental.runBlocking
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import tech.summerly.weather.await
import tech.summerly.weather.data.local.RegionDataBase
import tech.summerly.weather.model.UserRegion
import tech.summerly.weather.model.UserRegion.Companion.ID_NONE

@RunWith(AndroidJUnit4::class)
class UserRegionDaoTest {
    private val database = Room
            .inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(), RegionDataBase::class.java)
            .allowMainThreadQueries()
            .build()

    private val userRegionDao = database.userRegionDao()


    @Test
    fun getRegionsWhenIsEmpty() = runBlocking {
        val regions = userRegionDao.getTotalUserRegions().await()
        assertTrue(regions == null || regions.isEmpty())
    }

    @Test
    fun insertRegionsWhenIsEmpty() = runBlocking {
        val region = userRegionDao.insertUserRegion(UserRegion(0, "region1", null, -1, -1))
        assertInTable(region)
    }


    @Test
    fun insertRegionToHead() = runBlocking {
        val id1 = userRegionDao.insertUserRegion(UserRegion(0, "region1", null, -1, -1))
        assertInTable(id1)

        val id0 = userRegionDao.insertUserRegion(UserRegion(0, "region0", null, id1, -1))
        assertInTable(id0)

        assertSort(ID_NONE, id0)
        assertSort(id0, id1)
        assertSort(id1, ID_NONE)
    }


    @Test
    fun insertRegionToTail() = runBlocking {
        val id1 = userRegionDao.insertUserRegion(UserRegion(0, "region1", null, -1, -1))
        assertInTable(id1)

        val id2 = userRegionDao.insertUserRegion(UserRegion(0, "region2", null, -1, -1))
        assertInTable(id2)

        assertSort(ID_NONE, id1)
        assertSort(id1, id2)
        assertSort(id2, ID_NONE)
    }

    @Test
    fun insertRegionToCenter() {


        val region0 = userRegionDao.insertUserRegion(UserRegion(0, "region0", null, -1, -1))
        val region2 = userRegionDao.insertUserRegion(UserRegion(0, "region2", null, -1, -1))
        assertSort(region0, region2)

        val region1 = userRegionDao.insertUserRegion(UserRegion(0, "region1", null, region2, region0))

        assertSort(region0, region1, region2)

    }

    @Test
    fun testMove() {
        //当前顺序为 region0,center,region2
        val region0 = userRegionDao.insertUserRegion(UserRegion(0, "region0", null, -1, -1))
        val region1 = userRegionDao.insertUserRegion(UserRegion(0, "region1", null, -1, -1))
        val region2 = userRegionDao.insertUserRegion(UserRegion(0, "region2", null, -1, -1))

        assertSort(region0, region1, region2)

        //将 region0 移动到 region1 和 region2 之间
        userRegionDao.moveUserRegion(userRegionDao.getUserRegion(region0)!!, region1, region2)

        //断言顺序已经改变为 region1,region0,region2
        assertSort(region1, region0, region2)
    }


    private fun assertInTable(id: Long) {
        userRegionDao.getUserRegion(id)!!
    }

    private fun assertSort(vararg ids: Long) {
        for (i in 0 until ids.size) {
            when (i) {
                0 -> assertSort(ID_NONE, ids[i])
                ids.size - 1 -> assertSort(ids[i], ID_NONE)
                else -> assertSort(ids[i], ids[i + 1])
            }
        }
    }

    /**
     * assert the item of [behind] is behind the item of [front]
     */
    private fun assertSort(front: Long, behind: Long) {
        val previous = userRegionDao.getUserRegion(front)
        val after = userRegionDao.getUserRegion(behind)

        when {
            previous == null -> {
                after!!
                assertTrue(after.previous == ID_NONE)
            }
            after == null -> assertTrue(previous.next == ID_NONE)
            else -> {
                assertTrue(previous.next == after.id)
                assertTrue(after.previous == previous.id)
            }
        }

    }

    @After
    fun closeDb() {
        database.close()
    }
}